/*
 * 角色类（L2character）内部的HP/MP状态处理对象
 */

package labox.innovation.gameserver.model.actor.status;

import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.stat.CharStat;
import labox.innovation.gameserver.skills.Formulas;
import labox.innovation.gameserver.timer.Timer;
import labox.innovation.util.Rnd;

public class CharStatus {

	protected static final Logger _log = LoggerFactory.getLogger(CharStatus.class.getName());

	protected L2Character _activeChar;//拥有此对象的L2Character

	private int _currentHp; //当前的HP值

	private int _currentMp; //当前的MP值

	//包含所有需要被通知 HP/MP的更新的L2Character 数组
	private List<L2Character> _statusListener;

	private Timer _regTask;

	public CharStatus(L2Character activeChar) {
		_activeChar = activeChar;
	}

	/**
	 * 开始 HP/MP 自恢复调度任务.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Calculate the regen task period</li> <li>Launch the HP/MP/CP Regeneration task with Medium priority</li><BR>
	 * <BR>
	 * 
	 */
	public final void startRegeneration() {
		if (getActiveChar().isDead())
			return;
		//获得角色的回血调度时间（ms）
		int period = Formulas.getRegeneratePeriod(getActiveChar());
		// Create the HP/MP Regeneration task
		if (_regTask == null)
			_regTask = new Timer(period, true, new Runnable() {
				public void run() {
					doRegeneration();
				}
			});
		if(!_regTask.isAlive())
			_regTask.start();

	}

	/**
	 * 停止HP/MP自恢复调度任务.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Set the RegenActive flag to False</li> <li>Stop the HP/MP/CP Regeneration task</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopRegeneration() {
		if (_regTask != null) {
			_regTask.cancel();
		}
	}

	/**
	 * 
	 * 把需要通知HP/MP更新变化的L2Character加入状态监听列表
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * Each L2Character owns a list called <B>_statusListener</B> that contains all L2PcInstance to inform of HP/MP updates. Players who must be informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send Server->Client packet StatusUpdate.<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Target a PC or NPC</li><BR>
	 * <BR>
	 * 
	 * @param object
	 *            L2Character to add to the listener
	 * 
	 */
	public final void addStatusListener(L2Character object) {
		if (object == _activeChar)
			return;
		getStatusListener().add(object);
	}

	/**
	 * 把需要通知HP/MP更新变化的L2Character从状态监听列表中删除
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * Each L2Character owns a list called <B>_statusListener</B> that contains all L2PcInstance to inform of HP/MP updates. Players who must be informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send Server->Client packet StatusUpdate.<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Untarget a PC or NPC</li><BR>
	 * <BR>
	 * 
	 * @param object
	 *            L2Character to add to the listener
	 * 
	 */
	public final void removeStatusListener(L2Character object) {
		getStatusListener().remove(object);
	}

	/**
	 * 返回需要通知HP/MP更新变化的L2Character的状态监听列表
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * Each L2Character owns a list called <B>_statusListener</B> that contains all L2PcInstance to inform of HP/MP updates. Players who must be informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send Server->Client packet StatusUpdate.<BR>
	 * <BR>
	 * 
	 * @return The list of L2Character to inform or null if empty
	 * 
	 */
	public final List<L2Character> getStatusListener() {
		if (_statusListener == null)
			_statusListener = new LinkedList<L2Character>();
		return _statusListener;
	}

	/**
	 * 
	 *减少L2Character的当前HP，如有必要启动doDie任务<BR>
	 * <BR>
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2Attackable : Set overhit values</li><BR>
	 * <li>L2Npc : Update the attacker AggroInfo of the L2Attackable _aggroList and clear duel status of the attacking players</li><BR>
	 * <BR>
	 * 
	 * @param i
	 *            The HP decrease value
	 * @param attacker
	 *            The L2Character who attacks
	 * @param awake
	 *            The awake state (If True : stop sleeping)
	 * 
	 */
	public void reduceHp(int value, L2Character attacker) {
		reduceHp(value, attacker, true, false, false);
	}

	public void reduceHp(int value, L2Character attacker, boolean isHpConsumption) {
		reduceHp(value, attacker, true, false, isHpConsumption);
	}

	/**
	 * 减少HP
	 * 
	 * @param value
	 *            要减少的值
	 * @param attacker
	 *            攻击者
	 * @param awake
	 *            是否唤醒
	 * @param isDOT
	 *            是否持续伤害
	 * @param isHPConsumption
	 *            是否HP消费
	 */
	public void reduceHp(int value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption) {
		L2Character activeChar = _activeChar;

		if (activeChar.isDead())//如果这个角色已经死亡
			return;

		if (activeChar.isInvul()) {//如果是不受伤害状态(无敌)
			if (attacker != activeChar)// 其他的角色是不能伤害到的
				return;
			//仅仅持续伤害和HP消费可以伤害自己
			if (!isDOT && !isHPConsumption)
				return;
		}

		if (attacker != null) {
			FuzePcInstance attackerPlayer = attacker.getActingPlayer();
			if (attackerPlayer != null && attackerPlayer.isGM() && !attackerPlayer.getAccessLevel().canGiveDamage())
				return;
		}

		if (!isDOT && !isHPConsumption) {
			if (awake && activeChar.isSleeping())//如果是唤醒并且角色在睡眠
				activeChar.stopSleeping(null);
			else if (activeChar.isStunned() && Rnd.get(10) == 0)//十分之一的概率结束震惊状态
				activeChar.stopStunning(null);
			else if (activeChar.isImmobileUntilAttacked())//解除攻击定身
				activeChar.stopImmobileUntilAttacked(null);
		}

		if (value > 0) {// 如果有HP就减少HP，并且减少的值一定要是整数
			int hp = getCurrentHp() - value;
			if (hp <= 0) {
				activeChar.doDie(attacker);//死亡处理
			} else
				setCurrentHp(hp);
		}
	}

	/**
	 * 减少MP
	 * 
	 * @param value
	 *            要减少的值
	 */
	public void reduceMp(int value) {
		setCurrentMp(Math.max(getCurrentMp() - value, 0));
	}

	public final int getCurrentHp() {
		int maxHp = _activeChar.getMaxHp();
		if (_currentHp > maxHp)
			_currentHp = maxHp;
		return _currentHp;
	}

	//Hp更新区间
	private int _hpUpdateIncCheck;

	private int _hpUpdateDecCheck;

	//mp更新间隔值
	private int _hpUpdateInterval;

	private int _mpUpdateIncCheck;

	private int _mpUpdateDecCheck;

	//mp更新间隔值
	private int _mpUpdateInterval;

	protected final int _barPixels = 46;

	public void initCharStatusUpdateValues() {
		_hpUpdateInterval = _activeChar.getMaxHp() / _barPixels; // MAX_HP div MAX_HP_BAR_PX HP更新间隔
		if (_hpUpdateInterval == 0) {
			_hpUpdateInterval = 1;
		}

		_hpUpdateInterval *= 3;
		_hpUpdateIncCheck = _activeChar.getMaxHp();
		_hpUpdateDecCheck = _activeChar.getMaxHp() - _hpUpdateInterval;

		_mpUpdateInterval = _activeChar.getMaxMp() / _barPixels; // MAX_HP div MAX_HP_BAR_PX HP更新间隔
		if (_mpUpdateInterval == 0) {
			_mpUpdateInterval = 1;
		}

		_mpUpdateInterval *= 3;
		_mpUpdateIncCheck = _activeChar.getMaxMp();
		_mpUpdateDecCheck = _activeChar.getMaxMp() - _hpUpdateInterval;
	}

	/**
	 * 返回是否需要HP更新
	 * 
	 * @return boolean
	 */
	protected boolean needHpUpdate(int barPixels) {
		int currentHp = getCurrentHp();

		//如果 当前HP小于1， 或者 当前最大的HP没有超出 显示的像素。  那么一直更新
		if (currentHp <= 1 || _activeChar.getMaxHp() < barPixels)
			return true;

		//如果当前Hp小于Hp更新的下限 或者 当前Hp大于Hp更新的上限
		//那么需要更新 Hp, 并且重新计算下次Hp更新的上下限
		if (currentHp <= _hpUpdateDecCheck || currentHp >= _hpUpdateIncCheck) {

			if (currentHp == _activeChar.getMaxHp()) {
				//如果当前Hp=最大的Hp, 那么下次Hp的更新上限为最大Hp+1; 下限为最大Hp- Hp的更新间隔值
				_hpUpdateIncCheck = currentHp + 1;
				_hpUpdateDecCheck = currentHp - _hpUpdateInterval;
			} else {
				//如果当前Hp不等于最大Hp。  那么计算出当前Hp是 更新间隔值的 倍率
				//下次Hp更新下限为 更新倍率*Hp更新间隔值. Hp更新上限值为下限值+更新间隔值
				int intMulti = currentHp / _hpUpdateInterval;

				_hpUpdateDecCheck = _hpUpdateInterval * intMulti;
				_hpUpdateIncCheck = _hpUpdateDecCheck + _hpUpdateInterval;
			}

			return true;
		}

		return false;
	}

	/**
	 * Returns true if mp update should be done, false if not
	 * 
	 * @return boolean
	 */
	private boolean needMpUpdate(int barPixels) {
		int currentMp = getCurrentMp();

		if (currentMp <= 1 || _activeChar.getMaxMp() < barPixels)
			return true;

		if (currentMp <= _mpUpdateDecCheck || currentMp >= _mpUpdateIncCheck) {
			if (currentMp == _activeChar.getMaxMp()) {
				_mpUpdateIncCheck = currentMp + 1;
				_mpUpdateDecCheck = currentMp - _mpUpdateInterval;
			} else {
				int doubleMulti = currentMp / _mpUpdateInterval;

				_mpUpdateDecCheck = _mpUpdateInterval * doubleMulti;
				_mpUpdateIncCheck = _mpUpdateDecCheck + _mpUpdateInterval;
			}

			return true;
		}

		return false;
	}

	/**
	 * 设置当前的HP并广播
	 * 
	 * @param newHp
	 *            新的HP
	 */
	public final void setCurrentHp(int newHp) {
		setCurrentHp(newHp, true);
	}

	public void setCurrentHp(int newHp, boolean broadcastPacket) {
		// 获取这个角色最大可能的HP
		if (_activeChar.isDead())
			return;
		int maxHp = _activeChar.getMaxHp();
		if (newHp >= maxHp) {
			_currentHp = maxHp;
		} else {
			_currentHp = newHp;
		}
		// Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform
		if (broadcastPacket && needHpUpdate(_barPixels))
			_activeChar.broadcastStatusUpdate();
	}

	public final void setCurrentHpMp(int newHp, int newMp) {
		setCurrentMp(newMp, false);
		setCurrentHp(newHp, true);//发送状态更新仅仅一次
	}

	public final int getCurrentMp() {
		int maxMp = _activeChar.getMaxMp();
		if (_currentMp > maxMp)
			_currentMp = maxMp;
		return _currentMp;
	}

	/**
	 * 设置当前的MP，并角色的状态广播
	 * 
	 * @param newMp
	 *            新的MP
	 */
	public final void setCurrentMp(int newMp) {
		setCurrentMp(newMp, true);
	}

	public final void setCurrentMp(int newMp, boolean broadcastPacket) {
		//获得这个角色的最大MP
		if (_activeChar.isDead())
			return;
		int maxMp = _activeChar.getMaxMp();
		if (newMp >= maxMp) {
			_currentMp = maxMp;
		} else {
			_currentMp = newMp;
		}

		// Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform
		if (broadcastPacket && needMpUpdate(_barPixels))
			_activeChar.broadcastStatusUpdate();
	}

	/**
	 * 自恢复任务，子类可以覆盖此方法
	 */
	protected void doRegeneration() {
		CharStat charstat = getActiveChar().getStat();
		int maxHp = charstat.getMaxHp();
		int maxMp = charstat.getMaxMp();
		int curHp = getCurrentHp();
		int curMp = getCurrentMp();

		// Modify the current HP of the L2Character and broadcast Server->Client packet StatusUpdate
		if (curHp < maxHp)
			setCurrentHp((int) (curHp + maxHp * 0.01f), false);

		// Modify the current MP of the L2Character and broadcast Server->Client packet StatusUpdate
		if (curMp < maxMp)
			setCurrentMp((int) (curMp + maxHp * 0.01f), false);

		if (!getActiveChar().isInActiveRegion()) {//如果不在激活区，满血后停止回复
			// no broadcast necessary for characters that are in inactive regions.
			// stop regeneration for characters who are filled up and in an inactive region.
			if (getCurrentHp() == maxHp && getCurrentMp() == maxMp)
				stopRegeneration();
		} else {//如果在激活区，检查HP、MP是否需要更新
			if (needHpUpdate(_barPixels) || needMpUpdate(_barPixels))
				_activeChar.broadcastStatusUpdate();
		}
	}

	public L2Character getActiveChar() {
		return _activeChar;
	}
}
